package com.yami.shop.im.socket;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yami.shop.bean.model.ShopDetail;
import com.yami.shop.bean.model.User;
import com.yami.shop.common.config.Constant;
import com.yami.shop.common.response.ServerResponseEntity;
import com.yami.shop.common.util.Json;
import com.yami.shop.im.bean.ImMsgBizUserHistory;
import com.yami.shop.im.bo.ShopSessionInfo;
import com.yami.shop.im.bo.SysSessionInfo;
import com.yami.shop.im.bo.UserSessionInfo;
import com.yami.shop.im.constants.MsgCode;
import com.yami.shop.im.constants.SendType;
import com.yami.shop.im.hanlder.SecurityHandler;
import com.yami.shop.im.param.MessageInfo;
import com.yami.shop.im.service.ImMsgBizUserHistoryService;
import com.yami.shop.im.vo.MsgItem;
import com.yami.shop.security.api.model.YamiUser;
import com.yami.shop.service.ShopDetailService;
import com.yami.shop.service.UserService;
import com.yami.shop.sys.comment.model.ShopEmployee;
import com.yami.shop.sys.comment.model.SysUser;
import com.yami.shop.sys.comment.service.ShopEmployeeService;
import com.yami.shop.sys.comment.service.SysUserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 用户收到的消息
 * 10,"无法获取用户信息"
 * 11,"账户已在别处登录，请刷新"
 * 12,"对方不在线"
 * 13, "内容格式有误"
 *
 * @author Lzk
 */
@ServerEndpoint("/im/websocket/user/{token}")
@Component
@Slf4j
public class UserWebSocketServer {

    /**
     * UUID为key
     */
    public static final ConcurrentHashMap<String, UserSessionInfo> USER_MAP = new ConcurrentHashMap<>(256);

    public static final ConcurrentHashMap<String, Long> IN_CHARGE_MAP = new ConcurrentHashMap<>(256);

    private static final String USER_PREFIX = "USER_";

    private static final String SYS_PREFIX = "SYS_";

    private static final String SHOP_EMPLOYEE_PREFIX = "SHOP_EMPLOYEE_";

    private static final String UNDERLINE = "_";

    private static final String IN_CHARGE = "IN_CHARGE_";

    private static UserService userService;

    private static SecurityHandler securityHandler;

    private static ShopDetailService shopDetailService;

    private static SysUserService sysUserService;

    private static ImMsgBizUserHistoryService imMsgBizUserHistoryService;

    private static ShopEmployeeService shopEmployeeService;

    public static void setApplicationContext(ApplicationContext applicationContext) {
        userService = applicationContext.getBean(UserService.class);
        securityHandler = applicationContext.getBean(SecurityHandler.class);
        shopDetailService = applicationContext.getBean(ShopDetailService.class);
        sysUserService = applicationContext.getBean(SysUserService.class);
        imMsgBizUserHistoryService = applicationContext.getBean(ImMsgBizUserHistoryService.class);
        shopEmployeeService = applicationContext.getBean(ShopEmployeeService.class);
    }

    /**
     * 连接建立成功调用的方法
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("token") String token) throws IOException {
        log.info("创建连接");
        YamiUser yamiUser = securityHandler.getUser(token);
        if (yamiUser == null) {
            session.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.fail(10, "无法获取用户信息")));
            return;
        }

        // 如果原来就有token
        UserSessionInfo oldUserSessionInfo = USER_MAP.get(USER_PREFIX + yamiUser.getUserId());
        if (oldUserSessionInfo != null) {
            // 客户端收到301，关闭ws链接，弹出提示
            if (oldUserSessionInfo.getSession().isOpen()) {
                oldUserSessionInfo.getSession().getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.fail(11, "账户已在别处登录，请刷新")));
                oldUserSessionInfo.getSession().close();
            }
        }

        User user = userService.getUserByUserId(yamiUser.getUserId());

        UserSessionInfo userSessionInfo = new UserSessionInfo();
        userSessionInfo.setUserId(yamiUser.getUserId());
        userSessionInfo.setPic(user.getPic());
        userSessionInfo.setNickName(user.getNickName());
        userSessionInfo.setSession(session);

        USER_MAP.put(USER_PREFIX + yamiUser.getUserId(), userSessionInfo);
        session.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.success()));
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose(Session session, @PathParam("token") String token) throws IOException {
        log.info("关闭连接");
        YamiUser user = securityHandler.getUser(token);
        if (user == null) {
            return;
        }
        USER_MAP.remove(USER_PREFIX + user.getUserId());
    }

    /**
     * 连接错误调用的方法
     */
    @OnError
    public void onError(Session session, @PathParam("token") String token, Throwable error) throws IOException {
        log.error("发生错误");
        error.printStackTrace();
        YamiUser user = securityHandler.getUser(token);
        if (user == null) {
            return;
        }
        USER_MAP.remove(USER_PREFIX + user.getUserId());
    }

    /**
     * 收到客户端消息后调用的方法，该消息由用户发过来
     *
     * @param sourceMessage 客户端发送过来的消息
     */
    @OnMessage
    public void onMessage(String sourceMessage, @PathParam("token") String token, Session session) throws IOException {
        // from user info
        YamiUser yamiUser = securityHandler.getUser(token);
        if (Objects.isNull(yamiUser)) {
            log.error("无法获取用户信息");
            session.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.fail(10, "无法获取用户信息")));
            return;
        }

        if (StrUtil.isBlank(sourceMessage)) {
            session.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.fail(13, "内容格式有误")));
            return;
        }

        log.info("sourceMessage: " + sourceMessage);

        MessageInfo messageInfo = Json.parseObject(sourceMessage, MessageInfo.class);

        if (Objects.isNull(messageInfo)) {
            session.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.fail(13, "内容格式有误")));
            return;
        }

        // 保持心跳
        if (StrUtil.equals(messageInfo.getContent(), Constant.HEART_BEAT)) {
            session.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.success(0, "保持心跳")));
            return;
        }

        Long bizId = Long.valueOf(messageInfo.getToId());
        String userId = yamiUser.getUserId();
        UserSessionInfo userSessionInfo = USER_MAP.get(USER_PREFIX + userId);

        // 判断用户是发送给商家客服还是平台客服
        switch (messageInfo.getSendType()) {
            case SendType.PLATFORM:
                sendToPlatform(bizId, session, messageInfo, userId, userSessionInfo);
                break;
            case SendType.SHOP:
                sendToShop(bizId, session, messageInfo, userId, userSessionInfo);
                break;
            default:
                break;
        }
    }

    public void sendToPlatform(Long bizId, Session session, MessageInfo messageInfo, String userId, UserSessionInfo userSessionInfo) throws IOException {
        SysUser sysUser = sysUserService.getSysUserById(bizId);
        if (Objects.isNull(sysUser)) {
            session.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.fail(14, "参数有误")));
            return;
        }

        // 获取消息历史
        if (Objects.nonNull(messageInfo.getHistory()) && Objects.equals(messageInfo.getHistory(), true)) {
            Page<MsgItem> msgItemPage = imMsgBizUserHistoryService.getUserPlatformHistory(sysUser, messageInfo, bizId, userId, userSessionInfo);
            session.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.success(MsgCode.HISTORY, msgItemPage)));
            return;
        }

        // 标记消息已读
        if (Objects.nonNull(messageInfo.getRead()) && Objects.equals(messageInfo.getRead(), true)) {
            if (Objects.nonNull(messageInfo.getMsgIds()) && !messageInfo.getMsgIds().isEmpty()) {
                List<ImMsgBizUserHistory> imMsgBizUserHistories = imMsgBizUserHistoryService.markUserPlatformRead(messageInfo, bizId, userId);
                if (!imMsgBizUserHistories.isEmpty()) {
                    session.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.success()));
                    SysSessionInfo sysSessionInfo = SysWebSocketServer.SYS_MAP.get(SYS_PREFIX + bizId);
                    if (Objects.nonNull(sysSessionInfo)) {
                        sysSessionInfo.getSession().getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.success(MsgCode.READ, messageInfo.getMsgIds())));
                    }
                }
            }
            return;
        }

        // 发送消息
        if (Objects.nonNull(messageInfo.getContent()) && !messageInfo.getContent().isEmpty()) {
            // 在线的话发送消息，不在线的话留言
            SysSessionInfo sysSessionInfo = SysWebSocketServer.SYS_MAP.get(SYS_PREFIX + bizId);
            if (Objects.nonNull(sysSessionInfo)) {
                ImMsgBizUserHistory imMsgBizUserHistory = imMsgBizUserHistoryService.saveUserPlatformHistory(messageInfo, bizId, userId);
                // 发送消息给平台
                MsgItem msgItem = imMsgBizUserHistoryService.getUserPlatformMsgItem(sysSessionInfo, userSessionInfo, imMsgBizUserHistory);
                sysSessionInfo.getSession().getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.success(msgItem)));
            } else {
                // 回复消息给用户
                ImMsgBizUserHistory imMsgBizUserHistory = imMsgBizUserHistoryService.saveUserPlatformOfflineReply(messageInfo, bizId, userId);
                sysSessionInfo = new SysSessionInfo();
                sysSessionInfo.setUserId(bizId);
                sysSessionInfo.setNickName(sysUser.getNickName());
                MsgItem msgItem = imMsgBizUserHistoryService.getUserPlatformMsgItem(sysSessionInfo, userSessionInfo, imMsgBizUserHistory);
                session.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.success(msgItem)));
            }
        }
    }

    public void sendToShop(Long bizId, Session session, MessageInfo messageInfo, String userId, UserSessionInfo userSessionInfo) throws IOException {
        ShopDetail shopDetail = shopDetailService.getShopDetailByShopId(bizId);
        if (Objects.isNull(shopDetail)) {
            session.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.fail(14, "参数有误")));
            return;
        }

        // 获取消息历史
        if (Objects.nonNull(messageInfo.getHistory()) && Objects.equals(messageInfo.getHistory(), true)) {
            Page<MsgItem> msgItemPage = imMsgBizUserHistoryService.getUserShopHistory(shopDetail, messageInfo, bizId, userId, userSessionInfo);
            session.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.success(MsgCode.HISTORY, msgItemPage)));
            return;
        }

        // 标记消息已读
        if (Objects.nonNull(messageInfo.getRead()) && Objects.equals(messageInfo.getRead(), true)) {
            if (Objects.nonNull(messageInfo.getMsgIds()) && !messageInfo.getMsgIds().isEmpty()) {
                List<ImMsgBizUserHistory> imMsgBizUserHistories = imMsgBizUserHistoryService.markUserShopRead(messageInfo, bizId, userId);
                if (!imMsgBizUserHistories.isEmpty()) {
                    session.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.success()));
                    if (Objects.nonNull(messageInfo.getReadNoticeEmployeeId())) {
                        ShopSessionInfo shopSessionInfo = ShopWebSocketServer.SHOP_EMPLOYEE_MAP.get(SHOP_EMPLOYEE_PREFIX + bizId + UNDERLINE + messageInfo.getReadNoticeEmployeeId());
                        if (Objects.nonNull(shopSessionInfo)) {
                            shopSessionInfo.getSession().getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.success(MsgCode.READ, messageInfo.getMsgIds())));
                        }
                    } else {
                        Long employeeId = IN_CHARGE_MAP.get(IN_CHARGE + bizId + UNDERLINE + userId);
                        if (Objects.nonNull(employeeId)) {
                            ShopSessionInfo shopSessionInfo = ShopWebSocketServer.SHOP_EMPLOYEE_MAP.get(SHOP_EMPLOYEE_PREFIX + bizId + UNDERLINE + employeeId);
                            if (Objects.nonNull(shopSessionInfo)) {
                                shopSessionInfo.getSession().getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.success(MsgCode.READ, messageInfo.getMsgIds())));
                            }
                        }
                    }
                }
            }
            return;
        }

        // 发送消息
        if (Objects.nonNull(messageInfo.getContent()) && !messageInfo.getContent().isEmpty()) {
            // 查出负责该客户的客服
            Long employeeId = IN_CHARGE_MAP.get(IN_CHARGE + bizId + UNDERLINE + userId);
            if (Objects.nonNull(employeeId)) {
                ShopSessionInfo shopSessionInfo = ShopWebSocketServer.SHOP_EMPLOYEE_MAP.get(SHOP_EMPLOYEE_PREFIX + bizId + UNDERLINE + employeeId);
                if (Objects.nonNull(shopSessionInfo)) {
                    ImMsgBizUserHistory imMsgBizUserHistory = imMsgBizUserHistoryService.saveUserShopHistory(messageInfo, bizId, userId, employeeId);
                    // 发送消息给客服
                    MsgItem msgItem = imMsgBizUserHistoryService.getUserShopMsgItem(shopSessionInfo, userSessionInfo, imMsgBizUserHistory);
                    shopSessionInfo.getSession().getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.success(msgItem)));
                    return;
                }
                IN_CHARGE_MAP.remove(IN_CHARGE + bizId + UNDERLINE + userId);
            }
            // 如果没有跟进的客服或者跟进的客服不在线
            Set<String> set = ShopWebSocketServer.SHOP_EMPLOYEE_MAP.keySet();
            List<ShopSessionInfo> listEmployee = new ArrayList<>();
            for (String key : set) {
                if (key.startsWith(SHOP_EMPLOYEE_PREFIX + bizId + UNDERLINE)) {
                    listEmployee.add(ShopWebSocketServer.SHOP_EMPLOYEE_MAP.get(key));
                }
            }
            if (!listEmployee.isEmpty()) {
                // 随机选一个在线客服
                int index = (int) (Math.random() * listEmployee.size());
                ShopSessionInfo shopSessionInfo = listEmployee.get(index);
                IN_CHARGE_MAP.put(IN_CHARGE + bizId + UNDERLINE + userId, shopSessionInfo.getEmployeeId());
                Map<String, ImMsgBizUserHistory> map = imMsgBizUserHistoryService.saveUserShopFirstReply(messageInfo, shopSessionInfo, bizId, userId);
                MsgItem msgItem = imMsgBizUserHistoryService.getUserShopMsgItem(shopSessionInfo, userSessionInfo, map.get("user"));
                shopSessionInfo.getSession().getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.success(msgItem)));
                // 回复消息给用户，客服
                MsgItem msgItem2 = imMsgBizUserHistoryService.getUserShopMsgItem(shopSessionInfo, userSessionInfo, map.get("shop"));
                session.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.success(msgItem2)));
                shopSessionInfo.getSession().getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.success(msgItem2)));
            } else {
                ImMsgBizUserHistory imMsgBizUserHistory = imMsgBizUserHistoryService.saveUserShopOfflineReply(messageInfo, bizId, userId);
                // 发送消息给用户
                ShopSessionInfo shopSessionInfo = new ShopSessionInfo();
                shopSessionInfo.setShopName(shopDetail.getShopName());
                shopSessionInfo.setPic(shopDetail.getShopLogo());
                shopSessionInfo.setShopId(bizId);
                ShopEmployee shopEmployee = shopEmployeeService.getOne(new LambdaQueryWrapper<ShopEmployee>().eq(ShopEmployee::getShopId, bizId).eq(ShopEmployee::getType, 0));
                shopSessionInfo.setEmployeeId(shopEmployee.getEmployeeId());
                shopSessionInfo.setEmployeeNickname(shopEmployee.getNickname());
                MsgItem msgItem = imMsgBizUserHistoryService.getUserShopMsgItem(shopSessionInfo, userSessionInfo, imMsgBizUserHistory);
                session.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.success(msgItem)));
            }
        }
    }

}
